/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       objloader.cpp
 * Author:     karooolek
 * Created on: 2010-04-11
 *
 **********************************************************************************************************************/

#include "objloader.h"

namespace mGameEngine
{
namespace Graphics
{

OBJLoader::OBJLoader() :
    MeshLoader()
{
}

OBJLoader::OBJLoader(const String &filename) :
    MeshLoader(filename)
{
}

OBJLoader::~OBJLoader()
{
}

void OBJLoader::load()
{
    Vector<Vertex> vertices;
    Vector<Group> groups;
    Vector<Bone> bones;

    std::ifstream file;
    file.open(_filename.c_str());

    Vector<Vector3> positions(0);
    Vector<Vector3> normals(0);
    Vector<Vector2> texCoords(0);
    TexturedMaterial *lastMaterial = NULL;
    Map<String, Material *> materials;
    Map<uint, Map<uint, Map<uint, uint16 > > > indexTree;

    while(file)
    {
        char line[256];
        file.getline(line, 256, '\n');

        // comment line
        if(line[0] == '#')
        {
            continue;
        }

        if(line[0] == 'v')
        {
            // vertex normal
            if(line[1] == 'n')
            {
                Vector3 normal;
                sscanf(line + 2, "%f %f %f", &normal.x, &normal.y, &normal.z);
                normals.push_back(normal);
            }

            // vertex texcoords
            else if(line[1] == 't')
            {
                Vector2 texCoord;
                sscanf(line + 2, "%f %f", &texCoord.x, &texCoord.y);
                texCoords.push_back(texCoord);
            }
            
            // new vertex
            else
            {
                Vector3 position;
                sscanf(line + 2, "%f %f %f", &position.x, &position.y, &position.z);
                positions.push_back(position);
            }

        }

        // group data
        else if(line[0] == 'g')
        {
            Group g;
            g.name = String(line + 2);
            g.material = !groups.empty() ? groups.back().material : NULL;
            groups.push_back(g);
        }

        // load meterial library
        else if(strncmp(line, "mtllib ", 7) == 0)
        {
            char name[64];
            sscanf(line + 8, "%s", name);
            
            std::ifstream mtlfile;
            mtlfile.open(name);
            
            // TODO parse MTL file

            mtlfile.close();
        }

        // material data
        else if(strncmp(line, "usemtl ", 7) == 0)
        {
            if(groups.empty())
            {
                // error? bad file?
                continue;
            }

            Group &g = groups.back();
            g.material = materials[String(line + 7)];
        }

        // new material
        else if(strncmp(line, "newmtl ", 7) == 0)
        {
            materials[String(line + 7)] = lastMaterial = new TexturedMaterial();
        }

        // material data
        else if(line[0] == 'K')
        {
            if(!lastMaterial)
            {
                // error bad file?
                continue;
            }

            Color color;
            sscanf(line+2, "%f %f %f", &color.r, &color.g, &color.b);

            // ambient
            if(line[1] == 'a')
            {
                lastMaterial->setAmbientColor(color);
            }
            // diffuse
            else if(line[1] == 'd')
            {
                lastMaterial->setDiffuseColor(color);
            }
            // specular
            else if(line[1] == 's')
            {
                lastMaterial->setSpecularColor(color);
            }
        }
        // shininess
        else if(line[0] == 'N' && line[1] == 's')
        {
            if(!lastMaterial)
            {
                // error bad file?
                continue;
            }

            float sh;
            sscanf(line+2, "%f", &sh);
            lastMaterial->setShininess(sh);
        }
        // texture map
        else if(strncmp(line, "map_Ka ", 7) == 0)
        {
            if(!lastMaterial)
            {
                // error bad file?
                continue;
            }

            ImageLoader ld(String(line+7));
            ld.load();
            Texture *texture = new Texture(ld.getImage(), TEX_WRAP_CLAMP, TEX_FILTER_BILINEAR);
            ld.unload();
            lastMaterial->setTexture(texture);
        }

        // face data
        else if(line[0] == 'f')
        {
            // new face
            if(isspace(line[1]))
            {
                if(positions.empty() || groups.empty())
                {
                    // error? bad file?                
                    continue;
                }

                Group &g = groups.back();

                Vector<uint16> faceVerts;
                for(char *substr = line + 2; substr; substr = strchr(substr + 2, ' '))
                {
                    uint vpi(0), vni(0), vti(0);
                    sscanf(substr, "%d/%d/%d", &vpi, &vti, &vni);
                    
                    // cant read data...
                    if(!vpi && !vni && !vti)
                    {
                        continue;
                    }

                    uint16 i = indexTree[vpi][vni][vti];
                    
                    // create new vertex
                    if(i == 0)
                    {
                        Vertex v;
                        if(vpi > 0 && vpi <= positions.size()) v.position = positions[vpi-1];
                        if(vni > 0 && vni <= normals.size()) v.normal = normals[vni-1];
                        if(vti > 0 && vti <= texCoords.size()) v.texCoords[0] = texCoords[vti-1];
                        vertices.push_back(v);
                        
                        indexTree[vpi][vni][vti] = i = uint16(vertices.size());
                    }

                    faceVerts.push_back(i);
                }

                // too few vertices
                if(faceVerts.size() < 3)
                {
                    continue;
                }

                // add triangle faces
                for(uint i = 2; i != faceVerts.size(); ++i)
                {
                    Face f;
                    f.vertices[0] = faceVerts[0]-1;
                    f.vertices[1] = faceVerts[i-1]-1;
                    f.vertices[2] = faceVerts[i]-1;
                    g.faces.push_back(f);
                }
            }
        }
    }

    // close file
    file.close();

    // create mesh
    _mesh = new Mesh(vertices, groups, bones);
    _mesh->calculateTangents();

    // mesh is loaded
    _state = 1.0f;
}

}
}

